﻿#include <QTimer>
#include <QSharedPointer>
#include <QDebug>
#include <QElapsedTimer>
#include "JZCoCoroutine.h"

//JZCoCoroutine
JZCoCoroutine* JZCoCoroutine::m_current = nullptr;

JZCoCoroutine* JZCoCoroutine::current()
{
    Q_ASSERT(m_current);
    return m_current;
}

JZCoCoroutine::JZCoCoroutine(const std::function<void()> &func)
{
    m_ctx = createContext(this);
    m_func = func;
    m_interrupt = false;
    m_finish = false;
}

JZCoCoroutine::~JZCoCoroutine()
{
    destoryContext(m_ctx);
}

void JZCoCoroutine::quit()
{
    m_interrupt = true;
}

bool JZCoCoroutine::isFinish()
{
    return true;
}

void JZCoCoroutine::waitFinish()
{
    resume();
}

void JZCoCoroutine::yield()
{
    Q_ASSERT(m_current == this);
    m_current = nullptr;
    yieldContext(m_ctx);
}

void JZCoCoroutine::resume()
{
    Q_ASSERT(m_current != this);
    m_current = this;
    resumeContext(m_ctx);
}

//JZCoThread
JZCoThread::JZCoThread(std::function<void()> func)
{
    m_func = func;
}

void JZCoThread::run()
{
    m_func();
}

//JZCoScheduler
JZCoScheduler* JZCoScheduler::instance()
{
    static JZCoScheduler inst;
    return &inst;
}

JZCoScheduler::JZCoScheduler()
{
    m_ctx = nullptr;
}

JZCoScheduler::~JZCoScheduler()
{
    if(m_ctx)
        destoryMainContext(m_ctx);
}

void JZCoScheduler::init()
{
    m_ctx = createMainContext();
}

void JZCoScheduler::startTask(JZCoCoroutinePtr task)
{
    QTimer::singleShot(0, this, [task] {
        task->resume();
    });
    m_coList.push_back(task);
}

void JZCoScheduler::endTask(JZCoCoroutine *task)
{
    JZCoCoroutinePtr end_task;
    task->m_finish = true;
    for (int i = 0; i < m_coList.size(); i++)
    {
        if (m_coList[i].data() == task)
        {
            end_task = m_coList[i];
            m_coList.removeAt(i);
            break;
        }
    }
    task->yield();
}

JZCoMainContext* JZCoScheduler::context()
{
    return m_ctx;
}

void jzco_init()
{
    g_scheduler->init();
}

void jzco_yield()
{
    JZCoCoroutine::current()->yield();
}

void jzco_sleep(int ms)
{
    bool ok = false; //检测是否被其他事件唤醒
    JZCoCoroutine* task = JZCoCoroutine::current();
    QTimer::singleShot(ms, [task,&ok]{
        ok = true;
        task->resume();
    });

    QElapsedTimer t;
    t.start();
    task->yield();
    Q_ASSERT(ok);
}

void jzco_waitThread(std::function<void()> func)
{
    JZCoThread t(func);
    t.start();
    while (true)
    {
        if (t.isRunning())
            jzco_sleep(10);
        else
            break;
    }
}

void jzco_waitCoroutine(const QList<JZCoCoroutinePtr>& co_list)
{
    while (true)
    {
        bool all_finish = false;
        for (int i = 0; i < co_list.size(); i++)
        {
            if (!co_list[i]->isFinish())
            {
                all_finish = false;
                break;
            }
        }
        if (all_finish)
            return;

        jzco_sleep(20);
    }
}

JZCoCoroutinePtr jzco_spawn(const std::function<void()> &func)
{
    JZCoCoroutinePtr ptr =  JZCoCoroutinePtr(new JZCoCoroutine(func));
    g_scheduler->startTask(ptr);
    return ptr;
}
